numNotesPerOctave(12).

whiteKey(a,0).
whiteKey(b,2).
whiteKey(c,3).
whiteKey(d,5).
whiteKey(e,7).
whiteKey(f,8).
whiteKey(g,10).

accidental('bb',0).
accidental('b',1).
accidental('',2).
accidental('#',3).
accidental('x',4).

scale(L):-
	numNotesPerOctave(N),
	scale_help(L,N).
scale_help([],0).
scale_help([A|B],N):-
	numlist(1,N,L),
	member(A,L),
	M is N-A,
	scale_help(B,M).

scaleName([1,1,1,1,1,1,1,1,1,1,1,1],chromatic).
scaleName([2,2,1,2,2,2,1],major).
scaleName([2,1,2,2,1,2,2],minor).
scaleName([2,1,2,2,1,3,1],harmonicMinor).
scaleName([2,1,2,2,2,2,1],melodicMinor).

modeName(I,S,N):-
	scaleName(S,major),
	((I = 1, N = ionian);
	 (I = 2, N = dorian);
	 (I = 3, N = phrygian);
	 (I = 4, N = lydian);
	 (I = 5, N = mixolydian);
	 (I = 6, N = aeolian);
	 (I = 7, N = locrian)).
modeName(I,S,N):-
	scaleName(S,melodicMinor),
	((I = 1, N = minMaj);				% min+7
	 (I = 2, N = phrygian#6ORdorianb9);		% susb9
	 (I = 3, N = lydianAugmented);			% maj+5
	 (I = 4, N = lydianDominant);			% 7+11
	 (I = 5, N = mixolydianb13);			% b6b7
	 (I = 6, N = halfDiminishedORlocrian#2ORaeolianb5);	% min7b5
	 (I = 7, N = alteredORdiminishedWholeToneORsuperLocrian)).	% 7alt
modeName(I,S,N):-
	scaleName(S,harmonicMinor),
	((I = 1, N = ?);
	 (I = 2, N = locrian13);
	 (I = 3, N = ionian#5);
	 (I = 4, N = dorian#11);
	 (I = 5, N = mixolydianb9b13ORphrygianMajor);
	 (I = 6, N = lydian#9);
	 (I = 7, N = locrianb4bb7)).

mode(N,S,L):-
	scale(S),
	length(S,M),
	numlist(1,M,Ns),
	member(N,Ns),
	Q is N-1,
	shiftList(Q,S,L).
shiftList(0,L,L).
shiftList(N,[A|C],B):-
	N>0,
	M is N-1,
	append(C,[A],T),
	shiftList(M,T,B).
	

chroma([W,A]):-
	whiteKey(W,_),
	accidental(A,_).

note([C,O]):-
	chroma(C),
	octaveRange(LO,HO),
	numlist(LO,HO,L),
	member(O,L).

/* voice(name,low extreme, high extreme) -- must use octaves >= 1 ! */
voice(soprano,[[c,''],3],[[c,''],5]).
voice(alto,[[g,''],2],[[c,''],4]).
voice(tenor,[[c,''],2],[[g,''],3]).
voice(bass,[[e,''],1],[[g,''],2]).

octaveRange(LO,HO):-
	findall(L,voice(_,[_,L],_),Ls),
	findall(H,voice(_,_,[_,H]),Hs),
	sort(Ls,[LO|_]),
	sort(Hs,SHs),
	last(SHs,HO).

/*
	octave num breaks start at c's as follows:
	[bb0, a#0, cbb1], [b0, ax0, cb1], [c1, b#0, dbb1], [c#1, bx0, db1], d1, ..., b1, c2, d2, ...
	middle c = [c,3] = midi note num 40
*/

refNoteNum([[c,''],3],40).

raiseNumHalfSteps(A,B,H):-
	note(A),
	note(B),
	A=[[C1,A1],O1],
	B=[[C2,A2],O2],
	whiteKey(C1,N1),
	whiteKey(C2,N2),
	whiteKey(c,NR),
	D1 is N1-NR,		%white key dist from ref white key
	D2 is N2-NR,
	accidental(A1,M1),
	accidental(A2,M2),
	accidental('',MR),
	E1 is M1-MR,		%accidental dist from ref accidental
	E2 is M2-MR,
	T1 is D1+E1,
	T2 is D2+E2,
	numNotesPerOctave(N),
	(
		( ((D1>=0, D2>=0);(D1<0, D2<0)), H is ((T2-T1)+N*(O2-O1)) )
		;
		(D1>=0, D2<0, H is ((T2-T1)+N*(O2-O1+1)))
		;
		(D1<0, D2>=0, H is ((T2-T1)+N*(O2-O1-1)))
	).

noteNum(A,N):-
	refNoteNum(RA,RN),
	raiseNumHalfSteps(RA,A,M),
	N is M+RN.

allSpellingsOfNoteNum(N,L):-
	setof(M,noteNum(M,N),L).

allNoteNums(L):-
	setof([N,Ns],allSpellingsOfNoteNum(N,Ns),L).

allNoteSpellings(L):-
	setof(M,N^allSpellingsOfNote(N,M),L).

allSpellingsOfNote(N,L):-
	setof(A,raiseNumHalfSteps(A,N,0),L).

allSpellingsOfChroma(Co,L):-
	setof(C,O1^O2^raiseNumHalfSteps([C,O1],[Co,O2],0),L).

allChromaSpellings(L):-
	setof(Cs,C^(allSpellingsOfChroma(C,Cs)),L).

/* obsolete
raiseNote(A,N,B):-
	var(N),
	octaveRange(LO,HO),
	NO is HO-LO+1,
	numNotesPerOctave(NPO),
	H is NPO*NO,
	L is -1*H,
	numlist(-5,5,Ns), %(L,H,Ns),
	%writeln(Ns),
	member(N,Ns),
	writeln(N),
	raiseNote(A,N,B).
raiseNote(A,N,B):-
	N=0,
	samePitch(A,B).
raiseNote(A,N,B):-
	N>0,
	%writeln(N),
	note(A),
	note(B),
	M is N-1,
	successorNote(A,C),
	%writeln([A,C]),
	raiseNote(C,M,B).
raiseNote(A,N,B):-
	N<0,
	M is -1*N,
	raiseNote(B,M,A).

successorNote(A,B):-
	%note(A),
	%note(B),
	%writeln([A,B]),
	samePitch(A,[[C1,A1],O1]),
	accidental(A1,N),
	M is N+1,
	accidental(NA,M),
	samePitch([[C1,NA],O1],B),!.
*/

/* obsolete -- now use raiseNumHalfSteps(A,B,0)
samePitch(A,B):-
	note(A),
	note(B),
	A=[[C1,A1],O1],
	B=[[C2,A2],O2],
	whiteKey(C1,N1),
	whiteKey(C2,N2),
	whiteKey(c,NR),
	D1 is N1-NR,		%white key dist from ref white key
	D2 is N2-NR,
	accidental(A1,M1),
	accidental(A2,M2),
	accidental('',MR),
	E1 is M1-MR,		%accidental dist from ref accidental
	E2 is M2-MR,
	T1 is D1+E1,
	T2 is D2+E2,
	numNotesPerOctave(N),
	U1 is T1+N,
	U2 is T2+N,

	(
		(
			((D1=D2,E1=E2);					%(same spelling (actually redundant w/next line) or
			 ( ((D1>=0, D2>=0);(D1<0, D2<0)), T1=T2 )	%(same side of ref and add up to same)) and
			),
			O1=O2						%same octave
		);
		(
			D1>=0, D2<0, ((T1=T2, succ(O2,O1));(T1=U2, O1=O2))	%opposite sides of ref and octave correct
		);
		(
			D1<0, D2>=0, ((T1=T2, succ(O1,O2));(T2=U1, O1=O2))	%symmetry (better to call samePitch(B,A)?)
		)
	).
*/